home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / less / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-10  |  12.9 KB  |  707 lines

  1. /*
  2.  * Routines which deal with the characteristics of the terminal.
  3.  * Uses termcap to be as terminal-independent as possible.
  4.  *
  5.  * {{ Someday this should be rewritten to use curses. }}
  6.  */
  7.  
  8. #include "less.h"
  9. #if XENIX
  10. #include <sys/types.h>
  11. #include <sys/ioctl.h>
  12. #endif
  13.  
  14. #if TERMIO
  15. #include <termio.h>
  16. #else
  17. #include <sgtty.h>
  18. #endif
  19.  
  20. #if !TERMIO && defined(TIOCGWINSZ)
  21. #include <sys/ioctl.h>
  22. #else
  23. /*
  24.  * For the Unix PC (ATT 7300 & 3B1):
  25.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  26.  * whether to include sys/window.h.  Use SIGPHONE from signal.h instead.
  27.  */
  28. #include <signal.h>
  29. #ifdef SIGPHONE
  30. #include <sys/window.h>
  31. #endif
  32. #endif
  33.  
  34. #if NEED_PTEM_H && defined(TIOCGWINSZ)
  35. /*
  36.  * All this just to get struct winsize.  Sigh.
  37.  */
  38. #include <sys/types.h>
  39. #include <sys/stream.h>
  40. #include <sys/ptem.h>
  41. #endif
  42.  
  43. /*
  44.  * Strings passed to tputs() to do various terminal functions.
  45.  */
  46. static char
  47.     *sc_pad,        /* Pad string */
  48.     *sc_home,        /* Cursor home */
  49.     *sc_addline,        /* Add line, scroll down following lines */
  50.     *sc_lower_left,        /* Cursor to last line, first column */
  51.     *sc_move,        /* General cursor positioning */
  52.     *sc_clear,        /* Clear screen */
  53.     *sc_eol_clear,        /* Clear to end of line */
  54.     *sc_s_in,        /* Enter standout (highlighted) mode */
  55.     *sc_s_out,        /* Exit standout mode */
  56.     *sc_u_in,        /* Enter underline mode */
  57.     *sc_u_out,        /* Exit underline mode */
  58.     *sc_b_in,        /* Enter bold mode */
  59.     *sc_b_out,        /* Exit bold mode */
  60.     *sc_bl_in,        /* Enter blink mode */
  61.     *sc_bl_out,        /* Exit blink mode */
  62.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  63.     *sc_backspace,        /* Backspace cursor */
  64.     *sc_init,        /* Startup terminal initialization */
  65.     *sc_deinit;        /* Exit terminal de-initialization */
  66.  
  67. static int init_done = 0;
  68.  
  69. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  70. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  71. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  72. public int sc_width, sc_height;    /* Height & width of screen */
  73. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  74. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  75. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  76. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  77.  
  78. static char *cheaper();
  79.  
  80. /*
  81.  * These two variables are sometimes defined in,
  82.  * and needed by, the termcap library.
  83.  * It may be necessary on some systems to declare them extern here.
  84.  */
  85. /*extern*/ short ospeed;    /* Terminal output baud rate */
  86. /*extern*/ char PC;        /* Pad character */
  87.  
  88. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  89. extern int know_dumb;        /* Don't complain about a dumb terminal */
  90. extern int back_scroll;
  91. extern int swindow;
  92. extern char *tgetstr();
  93. extern char *tgoto();
  94. extern char *getenv();
  95.  
  96.  
  97. /*
  98.  * Change terminal to "raw mode", or restore to "normal" mode.
  99.  * "Raw mode" means 
  100.  *    1. An outstanding read will complete on receipt of a single keystroke.
  101.  *    2. Input is not echoed.  
  102.  *    3. On output, \n is mapped to \r\n.
  103.  *    4. \t is NOT expanded into spaces.
  104.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  105.  *       etc. are NOT disabled.
  106.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  107.  */
  108.     public void
  109. raw_mode(on)
  110.     int on;
  111. {
  112.     static int curr_on = 0;
  113.  
  114.     if (on == curr_on)
  115.         return;
  116. #if TERMIO
  117.     {
  118.     struct termio s;
  119.     static struct termio save_term;
  120.  
  121.     if (on)
  122.     {
  123.         /*
  124.          * Get terminal modes.
  125.          */
  126.         ioctl(2, TCGETA, &s);
  127.  
  128.         /*
  129.          * Save modes and set certain variables dependent on modes.
  130.          */
  131.         save_term = s;
  132.         ospeed = s.c_cflag & CBAUD;
  133.         erase_char = s.c_cc[VERASE];
  134.         kill_char = s.c_cc[VKILL];
  135.  
  136.         /*
  137.          * Set the modes to the way we want them.
  138.          */
  139.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  140.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  141.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  142.         s.c_cc[VMIN] = 1;
  143.         s.c_cc[VTIME] = 0;
  144.     } else
  145.     {
  146.         /*
  147.          * Restore saved modes.
  148.          */
  149.         s = save_term;
  150.     }
  151.     ioctl(2, TCSETAW, &s);
  152.     }
  153. #else
  154.     {
  155.     struct sgttyb s;
  156.     static struct sgttyb save_term;
  157.  
  158.     if (on)
  159.     {
  160.         /*
  161.          * Get terminal modes.
  162.          */
  163.         ioctl(2, TIOCGETP, &s);
  164.  
  165.         /*
  166.          * Save modes and set certain variables dependent on modes.
  167.          */
  168.         save_term = s;
  169.         ospeed = s.sg_ospeed;
  170.         erase_char = s.sg_erase;
  171.         kill_char = s.sg_kill;
  172.  
  173.         /*
  174.          * Set the modes to the way we want them.
  175.          */
  176.         s.sg_flags |= CBREAK;
  177.         s.sg_flags &= ~(ECHO|XTABS);
  178.     } else
  179.     {
  180.         /*
  181.          * Restore saved modes.
  182.          */
  183.         s = save_term;
  184.     }
  185.     ioctl(2, TIOCSETN, &s);
  186.     }
  187. #endif
  188.     curr_on = on;
  189. }
  190.  
  191.     static void
  192. cannot(s)
  193.     char *s;
  194. {
  195.     PARG parg;
  196.  
  197.     if (know_dumb)
  198.         /* 
  199.          * User knows this is a dumb terminal, so don't tell him.
  200.          */
  201.         return;
  202.  
  203.     parg.p_string = s;
  204.     error("WARNING: terminal cannot %s", &parg);
  205. }
  206.  
  207. /*
  208.  * Get size of the output screen.
  209.  */
  210.     public void
  211. scrsize(p_height, p_width)
  212.     int *p_height;
  213.     int *p_width;
  214. {
  215.     register char *s;
  216. #ifdef TIOCGWINSZ
  217.     struct winsize w;
  218. #else
  219. #ifdef WIOCGETD
  220.     struct uwdata w;
  221. #endif
  222. #endif
  223.  
  224. #ifdef TIOCGWINSZ
  225.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  226.         *p_height = w.ws_row;
  227.     else
  228. #else
  229. #ifdef WIOCGETD
  230.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  231.         *p_height = w.uw_height/w.uw_vs;
  232.     else
  233. #endif
  234. #endif
  235.     if ((s = getenv("LINES")) != NULL)
  236.         *p_height = atoi(s);
  237.     else
  238.          *p_height = tgetnum("li");
  239.  
  240.     if (*p_height <= 0)
  241.         *p_height = 24;
  242.  
  243. #ifdef TIOCGWINSZ
  244.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  245.         *p_width = w.ws_col;
  246.     else
  247. #ifdef WIOCGETD
  248.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  249.         *p_width = w.uw_width/w.uw_hs;
  250.     else
  251. #endif
  252. #endif
  253.     if ((s = getenv("COLUMNS")) != NULL)
  254.         *p_width = atoi(s);
  255.     else
  256.          *p_width = tgetnum("co");
  257.  
  258.      if (*p_width <= 0)
  259.           *p_width = 80;
  260. }
  261.  
  262. /*
  263.  * Get terminal capabilities via termcap.
  264.  */
  265.     public void
  266. get_term()
  267. {
  268.     char *sp;
  269.     register char *t1, *t2;
  270.     register int hard;
  271.     char *term;
  272.     char termbuf[2048];
  273.  
  274.     static char sbuf[1024];
  275.  
  276.     /*
  277.      * Find out what kind of terminal this is.
  278.      */
  279.      if ((term = getenv("TERM")) == NULL)
  280.          term = "unknown";
  281.      if (tgetent(termbuf, term) <= 0)
  282.          strcpy(termbuf, "dumb:hc:");
  283.  
  284.      hard = tgetflag("hc");
  285.  
  286.     /*
  287.      * Get size of the screen.
  288.      */
  289.     scrsize(&sc_height, &sc_width);
  290.     pos_init();
  291.     if (swindow < 0)
  292.         swindow = sc_height - 1;
  293.  
  294.     auto_wrap = tgetflag("am");
  295.     ignaw = tgetflag("xn");
  296.  
  297.     /*
  298.      * Assumes termcap variable "sg" is the printing width of:
  299.      * the standout sequence, the end standout sequence,
  300.      * the underline sequence, the end underline sequence,
  301.      * the boldface sequence, and the end boldface sequence.
  302.      */
  303.     if ((so_s_width = tgetnum("sg")) < 0)
  304.         so_s_width = 0;
  305.     so_e_width = so_s_width;
  306.  
  307.     bo_s_width = bo_e_width = so_s_width;
  308.     ul_s_width = ul_e_width = so_s_width;
  309.     bl_s_width = bl_e_width = so_s_width;
  310.  
  311.     /*
  312.      * Get various string-valued capabilities.
  313.      */
  314.     sp = sbuf;
  315.  
  316.     sc_pad = tgetstr("pc", &sp);
  317.     if (sc_pad != NULL)
  318.         PC = *sc_pad;
  319.  
  320.     sc_init = tgetstr("ti", &sp);
  321.     if (sc_init == NULL)
  322.         sc_init = "";
  323.  
  324.     sc_deinit= tgetstr("te", &sp);
  325.     if (sc_deinit == NULL)
  326.         sc_deinit = "";
  327.  
  328.     sc_eol_clear = tgetstr("ce", &sp);
  329.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  330.     {
  331.         cannot("clear to end of line");
  332.         sc_eol_clear = "";
  333.     }
  334.  
  335.     sc_clear = tgetstr("cl", &sp);
  336.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  337.     {
  338.         cannot("clear screen");
  339.         sc_clear = "\n\n";
  340.     }
  341.  
  342.     sc_move = tgetstr("cm", &sp);
  343.     if (hard || sc_move == NULL || *sc_move == '\0')
  344.     {
  345.         /*
  346.          * This is not an error here, because we don't 
  347.          * always need sc_move.
  348.          * We need it only if we don't have home or lower-left.
  349.          */
  350.         sc_move = "";
  351.     }
  352.  
  353.     sc_s_in = tgetstr("so", &sp);
  354.     if (hard || sc_s_in == NULL)
  355.         sc_s_in = "";
  356.  
  357.     sc_s_out = tgetstr("se", &sp);
  358.     if (hard || sc_s_out == NULL)
  359.         sc_s_out = "";
  360.  
  361.     sc_u_in = tgetstr("us", &sp);
  362.     if (hard || sc_u_in == NULL)
  363.         sc_u_in = sc_s_in;
  364.  
  365.     sc_u_out = tgetstr("ue", &sp);
  366.     if (hard || sc_u_out == NULL)
  367.         sc_u_out = sc_s_out;
  368.  
  369.     sc_b_in = tgetstr("md", &sp);
  370.     if (hard || sc_b_in == NULL)
  371.     {
  372.         sc_b_in = sc_s_in;
  373.         sc_b_out = sc_s_out;
  374.     } else
  375.     {
  376.         sc_b_out = tgetstr("me", &sp);
  377.         if (hard || sc_b_out == NULL)
  378.             sc_b_out = "";
  379.     }
  380.  
  381.     sc_bl_in = tgetstr("mb", &sp);
  382.     if (hard || sc_bl_in == NULL)
  383.     {
  384.         sc_bl_in = sc_s_in;
  385.         sc_bl_out = sc_s_out;
  386.     } else
  387.     {
  388.         sc_bl_out = tgetstr("me", &sp);
  389.         if (hard || sc_bl_out == NULL)
  390.             sc_bl_out = "";
  391.     }
  392.  
  393.     sc_visual_bell = tgetstr("vb", &sp);
  394.     if (hard || sc_visual_bell == NULL)
  395.         sc_visual_bell = "";
  396.  
  397.     if (tgetflag("bs"))
  398.         sc_backspace = "\b";
  399.     else
  400.     {
  401.         sc_backspace = tgetstr("bc", &sp);
  402.         if (sc_backspace == NULL || *sc_backspace == '\0')
  403.             sc_backspace = "\b";
  404.     }
  405.  
  406.     /*
  407.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  408.      * to move the cursor to the upper left corner of the screen.
  409.      */
  410.     t1 = tgetstr("ho", &sp);
  411.     if (hard || t1 == NULL)
  412.         t1 = "";
  413.     if (*sc_move == '\0')
  414.         t2 = "";
  415.     else
  416.     {
  417.         strcpy(sp, tgoto(sc_move, 0, 0));
  418.         t2 = sp;
  419.         sp += strlen(sp) + 1;
  420.     }
  421.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  422.  
  423.     /*
  424.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  425.      * to move the cursor to the lower left corner of the screen.
  426.      */
  427.     t1 = tgetstr("ll", &sp);
  428.     if (hard || t1 == NULL)
  429.         t1 = "";
  430.     if (*sc_move == '\0')
  431.         t2 = "";
  432.     else
  433.     {
  434.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  435.         t2 = sp;
  436.         sp += strlen(sp) + 1;
  437.     }
  438.     sc_lower_left = cheaper(t1, t2,
  439.         "move cursor to lower left of screen", "\r");
  440.  
  441.     /*
  442.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  443.      * to add a line at the top of the screen.
  444.      */
  445.     t1 = tgetstr("al", &sp);
  446.     if (hard || t1 == NULL)
  447.         t1 = "";
  448.     t2 = tgetstr("sr", &sp);
  449.     if (hard || t2 == NULL)
  450.         t2 = "";
  451.     sc_addline = cheaper(t1, t2, "scroll backwards", "");
  452.     if (*sc_addline == '\0')
  453.     {
  454.         /*
  455.          * Force repaint on any backward movement.
  456.          */
  457.         back_scroll = 0;
  458.     }
  459. }
  460.  
  461. /*
  462.  * Return the cost of displaying a termcap string.
  463.  * We use the trick of calling tputs, but as a char printing function
  464.  * we give it inc_costcount, which just increments "costcount".
  465.  * This tells us how many chars would be printed by using this string.
  466.  * {{ Couldn't we just use strlen? }}
  467.  */
  468. static int costcount;
  469.  
  470. /*ARGSUSED*/
  471.     static void
  472. inc_costcount(c)
  473.     int c;
  474. {
  475.     costcount++;
  476. }
  477.  
  478.     static int
  479. cost(t)
  480.     char *t;
  481. {
  482.     costcount = 0;
  483.     tputs(t, sc_height, inc_costcount);
  484.     return (costcount);
  485. }
  486.  
  487. /*
  488.  * Return the "best" of the two given termcap strings.
  489.  * The best, if both exist, is the one with the lower 
  490.  * cost (see cost() function).
  491.  */
  492.     static char *
  493. cheaper(t1, t2, doit, def)
  494.     char *t1, *t2;
  495.     char *doit;
  496.     char *def;
  497. {
  498.     if (*t1 == '\0' && *t2 == '\0')
  499.     {
  500.         cannot(doit);
  501.         return (def);
  502.     }
  503.     if (*t1 == '\0')
  504.         return (t2);
  505.     if (*t2 == '\0')
  506.         return (t1);
  507.     if (cost(t1) < cost(t2))
  508.         return (t1);
  509.     return (t2);
  510. }
  511.  
  512.  
  513. /*
  514.  * Below are the functions which perform all the 
  515.  * terminal-specific screen manipulation.
  516.  */
  517.  
  518.  
  519. /*
  520.  * Initialize terminal
  521.  */
  522.     public void
  523. init()
  524. {
  525.     tputs(sc_init, sc_height, putchr);
  526.     init_done = 1;
  527. }
  528.  
  529. /*
  530.  * Deinitialize terminal
  531.  */
  532.     public void
  533. deinit()
  534. {
  535.     if (!init_done)
  536.         return;
  537.     tputs(sc_deinit, sc_height, putchr);
  538.     init_done = 0;
  539. }
  540.  
  541. /*
  542.  * Home cursor (move to upper left corner of screen).
  543.  */
  544.     public void
  545. home()
  546. {
  547.     tputs(sc_home, 1, putchr);
  548. }
  549.  
  550. /*
  551.  * Add a blank line (called with cursor at home).
  552.  * Should scroll the display down.
  553.  */
  554.     public void
  555. add_line()
  556. {
  557.     tputs(sc_addline, sc_height, putchr);
  558. }
  559.  
  560. /*
  561.  * Move cursor to lower left corner of screen.
  562.  */
  563.     public void
  564. lower_left()
  565. {
  566.     tputs(sc_lower_left, 1, putchr);
  567. }
  568.  
  569. /*
  570.  * Ring the terminal bell.
  571.  */
  572.     public void
  573. bell()
  574. {
  575.     if (quiet == VERY_QUIET)
  576.         vbell();
  577.     else
  578.         putchr('\7');
  579. }
  580.  
  581. /*
  582.  * Output the "visual bell", if there is one.
  583.  */
  584.     public void
  585. vbell()
  586. {
  587.     if (*sc_visual_bell == '\0')
  588.         return;
  589.     tputs(sc_visual_bell, sc_height, putchr);
  590. }
  591.  
  592. /*
  593.  * Clear the screen.
  594.  */
  595.     public void
  596. clear()
  597. {
  598.     tputs(sc_clear, sc_height, putchr);
  599. }
  600.  
  601. /*
  602.  * Clear from the cursor to the end of the cursor's line.
  603.  * {{ This must not move the cursor. }}
  604.  */
  605.     public void
  606. clear_eol()
  607. {
  608.     tputs(sc_eol_clear, 1, putchr);
  609. }
  610.  
  611. /*
  612.  * Begin "standout" (bold, underline, or whatever).
  613.  */
  614.     public void
  615. so_enter()
  616. {
  617.     tputs(sc_s_in, 1, putchr);
  618. }
  619.  
  620. /*
  621.  * End "standout".
  622.  */
  623.     public void
  624. so_exit()
  625. {
  626.     tputs(sc_s_out, 1, putchr);
  627. }
  628.  
  629. /*
  630.  * Begin "underline" (hopefully real underlining, 
  631.  * otherwise whatever the terminal provides).
  632.  */
  633.     public void
  634. ul_enter()
  635. {
  636.     tputs(sc_u_in, 1, putchr);
  637. }
  638.  
  639. /*
  640.  * End "underline".
  641.  */
  642.     public void
  643. ul_exit()
  644. {
  645.     tputs(sc_u_out, 1, putchr);
  646. }
  647.  
  648. /*
  649.  * Begin "bold"
  650.  */
  651.     public void
  652. bo_enter()
  653. {
  654.     tputs(sc_b_in, 1, putchr);
  655. }
  656.  
  657. /*
  658.  * End "bold".
  659.  */
  660.     public void
  661. bo_exit()
  662. {
  663.     tputs(sc_b_out, 1, putchr);
  664. }
  665.  
  666. /*
  667.  * Begin "blink"
  668.  */
  669.     public void
  670. bl_enter()
  671. {
  672.     tputs(sc_bl_in, 1, putchr);
  673. }
  674.  
  675. /*
  676.  * End "blink".
  677.  */
  678.     public void
  679. bl_exit()
  680. {
  681.     tputs(sc_bl_out, 1, putchr);
  682. }
  683.  
  684. /*
  685.  * Erase the character to the left of the cursor 
  686.  * and move the cursor left.
  687.  */
  688.     public void
  689. backspace()
  690. {
  691.     /* 
  692.      * Try to erase the previous character by overstriking with a space.
  693.      */
  694.     tputs(sc_backspace, 1, putchr);
  695.     putchr(' ');
  696.     tputs(sc_backspace, 1, putchr);
  697. }
  698.  
  699. /*
  700.  * Output a plain backspace, without erasing the previous char.
  701.  */
  702.     public void
  703. putbs()
  704. {
  705.     tputs(sc_backspace, 1, putchr);
  706. }
  707.